package com.vans.practice.memorize_cache;

import lombok.SneakyThrows;
import org.apache.commons.lang3.concurrent.Computable;

import java.util.Map;
import java.util.concurrent.*;

/**
 * @Author elle
 * @Date 2020/10/3 22:38
 * @Description: 构建高效可伸缩的结果缓存
 * 问题描述： 高并发下， 计算结果； 值被重复计算； 高并发数据不一致问题
 * 问题解决： 这里使用ConcurrentHashMap来缓存结果体（保证线程安全）， 且为了避免重复计算， 使用FutureTask（保证其他线程可以知道计算结果是否计算完成或者正在计算）
 */
public class Memoizer<I, O> implements Computable<I, O> {

    private final Map<I,Future<O>> cache = new ConcurrentHashMap<I, Future<O>>();
    private final Computable<I, O> c;

    public Memoizer(Computable<I, O> c) {
        this.c = c;
    }

    /**
     * 实现了很好的并发性， 如果结果存在则立即返回
     * 不存在， 如果计算任务存在则阻塞等待返回为止， 不存在任务则新建执行， 继续等待值
     * @param i
     * @return
     * @throws InterruptedException
     */
    @SneakyThrows
    @Override
    public O compute(final I i) throws InterruptedException {
        Future<O> f = cache.get(i);
        if (null == f) {
            //接口匿名类， 定义计算任务
            Callable<O> task = new Callable<O>() {
                @Override
                public O call() throws Exception {
                    return c.compute(i);
                }
            };
            FutureTask<O> futureTask = new FutureTask<O>(task);
            f = futureTask;
            cache.putIfAbsent(i, futureTask); // 这里能保证不会出现重复计算的情况
            futureTask.run();//执行计算任务
        }
        try {
            return f.get();
        } catch (ExecutionException e) {
            throw e;
        }
    }
}
